package 多线程;

//import java.util.concurrent.atomic.AtomicInteger;

//import jdk.internal.misc.Unsafe;

//Compare and swap(CAS)
//当前的处理器基本都支持CAS，只不过每个厂家所实现的算法并不一样罢了，每一个CAS操作过程都包含三个参数：一个内存地址V，一个期望的值A和一个新值B，
//操作的时候如果这个地址上存放的值等于这个期望的值A，则将地址上的值赋为新值B，否则不做任何操作。CAS的基本思路就是，如果这个地址上的值和期望的值
//相等，则给其赋予新值，否则不做任何事儿，但是要返回原值是多少。
//
//我们先看一段代码：
/*public class CAS操作{
	public static int count=0;
	public static void main(String[] args) {
		//开启两个线程
		for (int i=0;i<2;i++) {
			new Thread(()->{
				try {
					Thread.sleep(10);
				}catch(InterruptedException e) {
					e.printStackTrace();
				}
				//每个线程count增加100
				for(int j=0;j<100;j++) {
					count++;
				}
			}).start();
		}
		try {
			Thread.sleep(2000);
		}catch(InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("count="+count);//count=101,结果大多数小于200
	}
}*/

//启动两个线程，每个线程中让静态变量count循环累加100次。
//最终输出的count结果一定是200吗？因为这段代码是非线程安全的，所以最终的自增结果很可能会小于200。我们再加上synchronized同步锁，再来看一下。
/*public class CAS操作{
	public static int count=0;
	public static void main(String[] args) {
		//开启两个线程
		for (int i=0;i<2;i++) {
			new Thread(()->{
				try {
					Thread.sleep(10);
				}catch(InterruptedException e) {
					e.printStackTrace();
				}
				//每个线程count增加100
				for(int j=0;j<100;j++) {
					synchronized(CAS操作.class) {//加锁
						count++;
					}
				}
			}).start();
		}
		try {
			Thread.sleep(2000);
		}catch(InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("count="+count);//count=200,结果每次都是200
	}
}*/
//加了同步锁之后，count自增的操作变成了原子性操作，所以最终输出一定是count=200，代码实现了线程安全。虽然synchronized确保了线程安全，但是在某些
//情况下，这并不是一个最佳的选择,关键在于性能问题。
//
//synchronized关键字会让没有得到锁资源的线程进入BLOCKED状态，而后在争夺到锁资源后恢复为RUNNABLE状态，这个过程中涉及到操作系统用户模式和内核
//模式的转换，代价比较高。
//
//尽管JAVA 1.6为synchronized做了优化，增加了从偏向锁到轻量级锁再到重量级锁的过度，但是在最终转变为重量级锁之后，性能仍然比较低。所以面对这种情
//况，我们就可以使用java中的“原子操作类”。
//
//所谓原子操作类，指的是java.util.concurrent.atomic包下，一系列以Atomic开头的包装类。如AtomicBoolean，AtomicUInteger，AtomicLong。
//它们分别用于Boolean，Integer，Long类型的原子性操作。
//
//现在我们尝试使用AtomicInteger类：
import java.util.concurrent.atomic.AtomicInteger;
public class CAS操作{
	public static AtomicInteger count=new AtomicInteger();
	public static void main(String[] args) {
		//开启两个线程
		for (int i=0;i<2;i++) {
			new Thread(()->{
				try {
					Thread.sleep(10);
				}catch(InterruptedException e) {
					e.printStackTrace();
				}
				//每个线程count增加100
				for(int j=0;j<100;j++) {
					count.incrementAndGet();
					//count.getAndIncrement();
				}
			}).start();
		}
		try {
			Thread.sleep(2000);
		}catch(InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("count="+count.get());//count=200,结果每次都是200
	}
}
//使用AtomicInteger之后，最终的输出结果同样可以保证是200。并且在某些情况下，代码的性能会比synchronized更好。
//
//而Atomic操作类的底层正是用到了“CAS机制”。
//
//CAS是英文单词Compare and Swap的缩写，翻译过来就是比较并替换。
//
//CAS机制中使用了3个基本操作数：内存地址V，旧的预期值A，要修改的新值B。
//
//更新一个变量的时候，只有当变量的预期值A和内存地址V当中的实际值相同时，才会将内存地址V对应的值修改为B。
//
//我们看一个例子：
//
//\1. 在内存地址V当中，存储着值为10的变量。
//┌───┬───┬───┬───┐
//│   │10 │   │   │  内存地址V
//└───┴───┴───┴───┘
//
//\2. 此时线程1想把变量的值增加1.对线程1来说，旧的预期值A=10，要修改的新值B=11.
//┌───┬───┬───┬───┐
//│   │10 │   │   │  内存地址V
//└───┴───┴───┴───┘
// 线程1：A=10  B=11
//
//\3. 在线程1要提交更新之前，另一个线程2抢先一步，把内存地址V中的变量值率先更新成了11。
//┌───┬───┬───┬───┐
//│   │11 │   │   │  内存地址V
//└───┴───┴───┴───┘
//线程1：A=10  B=11   线程2:把变量值更新为11
//
//\4. 线程1开始提交更新，首先进行A和地址V的实际值比较，发现A不等于V的实际值，提交失败。
//┌───┬───┬───┬───┐
//│   │11 │   │   │  内存地址V
//└───┴───┴───┴───┘
//线程1：A=10  B=11    A!==V的值(10!=11),提交失败 
//线程2:把变量值更新为11
//
//\5. 线程1 重新获取内存地址V的当前值，并重新计算想要修改的值。此时对线程1来说，A=11，B=12。这个重新尝试的过程被称为自旋。
//┌───┬───┬───┬───┐
//│   │11 │   │   │  内存地址V
//└───┴───┴───┴───┘
//线程1：A=11  B=12
//
//\6. 这一次比较幸运，没有其他线程改变地址V的值。线程1进行比较，发现A和地址V的实际值是相等的。
//┌───┬───┬───┬───┐
//│   │11 │   │   │  内存地址V
//└───┴───┴───┴───┘
//线程1：A=11  B=12  A==V的值  (11==11)   
//
//\7. 线程1进行交换，把地址V的值替换为B，也就是12.
//┌───┬───┬───┬───┐
//│   │12 │   │   │  内存地址V
//└───┴───┴───┴───┘
//线程1：A=11  B=12   A==V的值  (11==11)
//地址V的值更新为12
//
//从思想上来说，synchronized属于悲观锁，悲观的认为程序中的并发情况严重，所以严防死守，CAS属于乐观锁，乐观地认为程序中的并发情况不那么严重，
//所以让线程不断去重试更新。
//
//在java中除了上面提到的Atomic系列类，以及Lock系列类夺得底层实现，甚至在JAVA1.6以上版本，synchronized转变为重量级锁之前，也会采用CAS机制。
//
//CAS的缺点：
//1） CPU开销过大
//在并发量比较高的情况下，如果许多线程反复尝试更新某一个变量，却又一直更新不成功，循环往复，会给CPU带来很到的压力。
//
//2） 不能保证代码块的原子性
//CAS机制所保证的只是一个变量的原子性操作，而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新，就不得不使用synchronized了。
//
//3） ABA问题
//这是CAS机制最大的问题所在。（后面有介绍）
//
//我们下面来介绍一下两个问题：
//
//  1.JAVA中CAS的底层实现
//  2.CAS的ABA问题和解决办法。
//我们看一下AtomicInteger当中常用的自增方法incrementAndGet：
//
//public final int incrementAndGet() {
//  for (;;) {
//    int current = get();
//    int next = current + 1;
//    if (compareAndSet(current, next))
//      return next;
//  }
//}
//
//private volatile int value; 
//public final int get() {
//  return value;
//}
//这段代码是一个无限循环，也就是CAS的自旋，循环体中做了三件事：
//
//\1. 获取当前值
//
//\2. 当前值+1，计算出目标值
//
//\3. 进行CAS操作，如果成功则跳出循环，如果失败则重复上述步骤
//
//这里需要注意的重点是get方法，这个方法的作用是获取变量的当前值。
//
//如何保证获取的当前值是内存中的最新值？很简单，用volatile关键字来保证（保证线程间的可见性）。我们接下来看一下compareAndSet方法的实现：
//
//private static final Unsafe U = Unsafe.getUnsafe();
//private static final long VALUE= U.objectFieldOffset(AtomicInteger.class, "value");
//public final boolean compareAndSet(int expectedValue, int newValue) {
//	return U.compareAndSetInt(this, VALUE, expectedValue, newValue);
//	}
//
//public final class Unsafe {
//	...
//	public long objectFieldOffset(Class<?> c, String name) {
//		if (c == null || name == null) {
//			throw new NullPointerException();
//		}
//
//		return objectFieldOffset1(c, name);
//	}
//	...
//}
//compareAndSet方法的实现很简单，只有一行代码。这里涉及到两个重要的对象，一个是unsafe，一个是valueOffset。
//
//什么是unsafe呢？Java语言不像C，C++那样可以直接访问底层操作系统，但是JVM为我们提供了一个后门，这个后门就是unsafe。unsafe为我们提供了硬件级别的原子操作。
//
//至于valueOffset对象，是通过unsafe.objectFiledOffset方法得到，所代表的是AtomicInteger对象value成员变量在内存中的偏移量。我们可以简单的
//把valueOffset理解为value变量的内存地址。
//
//我们上面说过，CAS机制中使用了3个基本操作数：内存地址V，旧的预期值A，要修改的新值B。
//
//而unsafe的compareAndSwapInt方法的参数包括了这三个基本元素：valueOffset参数代表了V，expect参数代表了A，update参数代表了B。
//
//正是unsafe的compareAndSwapInt方法保证了Compare和Swap操作之间的原子性操作。
//
//我们现在来说什么是ABA问题。假设内存中有一个值为A的变量，存储在地址V中。
//┌───┬───┬───┬───┐
//│   │ A │   │   │  内存地址V
//└───┴───┴───┴───┘
//
//此时有三个线程想使用CAS的方式更新这个变量的值，每个线程的执行时间有略微偏差。线程1和线程2已经获取当前值，线程3还未获取当前值。
//┌───┬───┬───┬───┐
//│   │ A │   │   │  内存地址V
//└───┴───┴───┴───┘
//  线程1:获取当前值A，期望更新为B
//  线程2：获取当前值A，期望更新为B
//  线程3：期望更新为A
//
//接下来，线程1先一步执行成功，把当前值成功从A更新为B；同时线程2因为某种原因被阻塞住，没有做更新操作；线程3在线程1更新之后，获取了当前值B。
//┌───┬───┬───┬───┐
//│   │ B │   │   │  内存地址V
//└───┴───┴───┴───┘
//线程1:获取当前值A，成功更新为B
//线程2:获取当前值A，期望更新为B，BLOCK
//线程3:获取当前值B，期望更新为A
//
//在之后，线程2仍然处于阻塞状态，线程3继续执行，成功把当前值从B更新成了A。
//┌───┬───┬───┬───┐
//│   │ A │   │   │  内存地址V
//└───┴───┴───┴───┘
//线程1:获取当前值A，成功更新为B，已返回
//线程2:获取“当前值”A，期望更新为B，BLOCK
//线程3:获取当前值B，成功更新为A
//
//最后，线程2终于恢复了运行状态，由于阻塞之前已经获得了“当前值A”，并且经过compare检测，内存地址V中的实际值也是A，所以成功把变量值A更新成了B。
//┌───┬───┬───┬───┐
//│   │ B │   │   │  内存地址V
//└───┴───┴───┴───┘
//线程1:获取当前值A，成功更新为B，已返回
//线程2:获取“当前值”A，成功更新为B
//线程3:获取当前值B，成功更新为A，已返回
//
//看起来这个例子没啥问题，但如果结合实际，就可以发现它的问题所在。
//
//我们假设一个提款机的例子。假设有一个遵循CAS原理的提款机，小灰有100元存款，要用这个提款机来提款50元。
//
//
//
//由于提款机硬件出了点问题，小灰的提款操作被同时提交了两次，开启了两个线程，两个线程都是获取当前值100元，要更新成50元。
//
//理想情况下，应该一个线程更新成功，一个线程更新失败，小灰的存款值被扣一次。
//
//
//
//线程1首先执行成功，把余额从100改成50.线程2因为某种原因阻塞。这时，小灰的妈妈刚好给小灰汇款50元。
//
//
//
//线程2仍然是阻塞状态，线程3执行成功，把余额从50改成了100。
//
//
//
//线程2恢复运行，由于阻塞之前获得了“当前值”100，并且经过compare检测，此时存款实际值也是100，所以会成功把变量值100更新成50。
//
//
//
//原本线程2应当提交失败，小灰的正确余额应该保持100元，结果由于ABA问题提交成功了。
//
//怎么解决呢？加个版本号就可以了。
//
//真正要做到严谨的CAS机制，我们在compare阶段不仅要比较期望值A和地址V中的实际值，还要比较变量的版本号是否一致。
//
//我们仍然以刚才的例子来说明，假设地址V中存储着变量值A，当前版本号是01。线程1获取了当前值A和版本号01，想要更新为B，但是被阻塞了。
//
//
//
//这时候，内存地址V中变量发生了多次改变，版本号提升为03，但是变量值仍然是A。
//
//
//
//随后线程1恢复运行，进行compare操作。经过比较，线程1所获得的值和地址的实际值都是A，但是版本号不相等，所以这一次更新失败。
//
//
//
//在Java中，AtomicStampedReference类就实现了用版本号作比较额CAS机制
